package com.gitee.hljdrl.dsbridge.jsapi;

import android.os.Build;
import android.webkit.JavascriptInterface;

import androidx.annotation.Keep;

import com.ffsky.logger.L;
import com.gitee.hljdrl.dsbridge.basis.DSCallback;
import com.gitee.hljdrl.dsbridge.basis.DSResultListener;
import com.gitee.hljdrl.dsbridge.entity.DSCallInfo;

import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Keep
public class DSChannel extends BasicBridge {

    protected AtomicInteger mCallID = new AtomicInteger(0);

    //TODO 原生-->JS,loadUrl或加载中需要缓存调用，加载完成后【DSBridge JS 调用window._dsb.dsinit(Object obj)】
    protected final List<DSCallInfo> mDSCallInfoList = new LinkedList<>();

    protected Map<Integer, DSResultListener> mJsCallMap = new HashMap<>();

    protected boolean mDSInit = false;

    @Keep
    @JavascriptInterface
    public boolean hasNativeMethod(Object args) throws JSONException {
        L.i(TAG, "hasNativeMethod-->", args);
        JSONObject jsonObject = (JSONObject) args;
        String methodName = jsonObject.getString("name").trim();
        String type = jsonObject.getString("type").trim();
        String[] nameStr = parseNamespace(methodName);
        Object jsb = findDSPlugin(nameStr[0]);
        if (jsb != null) {
            Class<?> cls = jsb.getClass();
            boolean asyn = false;
            Method method = null;
            try {
                method = cls.getMethod(nameStr[1],
                        new Class[]{Object.class, DSCallback.class});
                asyn = true;
            } catch (Exception e) {
                try {
                    method = cls.getMethod(nameStr[1], new Class[]{Object.class});
                } catch (Exception ex) {

                }
            }
            if (method != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class);
                    if (annotation == null) {
                        return false;
                    }
                }
                if ("all".equals(type) || (asyn && "asyn".equals(type) || (!asyn && "syn".equals(type)))) {
                    return true;
                }

            }
        }
        return false;
    }

    @Keep
    @JavascriptInterface
    public String closePage(Object object) throws JSONException {
        L.i(TAG, "closePage-->", object);
        getWebViewEvent().closePage();
        return null;
    }

    @Keep
    @JavascriptInterface
    public void disableJavascriptDialogBlock(Object object) throws JSONException {
        L.i(TAG, "disableJavascriptDialogBlock-->", object);
        JSONObject jsonObject = (JSONObject) object;
        boolean alertBoxBlock = !jsonObject.getBoolean("disable");
        getWebViewEvent().setAlertBoxBlock(alertBoxBlock);
    }

    @Keep
    @JavascriptInterface
    public void dsinit(Object jsonObject) {
        L.i(TAG, "dsinit-->", jsonObject);
        mDSInit = true;
        dispatchStartupQueue();
    }

    @Keep
    @JavascriptInterface
    public void returnValue(final Object obj) {
        L.i(TAG, "returnValue-->", obj);
        getWebViewEvent().runOnMainThread(new Runnable() {
            @Override
            public void run() {
                JSONObject jsonObject = (JSONObject) obj;
                Object data = null;
                try {
                    final int id = jsonObject.getInt("id");
                    boolean isCompleted = jsonObject.getBoolean("complete");
                    DSResultListener handler = mJsCallMap.get(id);
                    if (jsonObject.has("data")) {
                        data = jsonObject.get("data");
                    }
                    if (handler != null) {
                        handler.onDSResult(data);
                        if (isCompleted) {
                            mJsCallMap.remove(id);
                        }
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void onLoadUrlBefore(String url) {
        super.onLoadUrlBefore(url);
        mDSCallInfoList.clear();
    }

    public void hasJavascriptMethod(String handlerName, DSResultListener<Boolean> existCallback) {
        evaluateScript("_hasJavascriptMethod", new Object[]{handlerName}, existCallback);
    }

    public <T> void callHandler(String method, DSResultListener<T> DSResultListener) {
        evaluateScript(method, null, DSResultListener);
    }

    public <T> void callHandler(String method, Object[] args, final DSResultListener<T> DSResultListener) {
        evaluateJavascript(method, args, DSResultListener);
    }

    public <T> void evaluateJavascript(String method, DSResultListener<T> DSResultListener) {
        evaluateScript(method, null, DSResultListener);
    }

    public <T> void evaluateJavascript(String method, Object[] args, final DSResultListener<T> DSResultListener) {
        DSCallInfo DSCallInfo = new DSCallInfo(method, mCallID.incrementAndGet(), args);
        if (DSResultListener != null) {
            mJsCallMap.put(DSCallInfo.callbackId, DSResultListener);
        }
        if (mDSCallInfoList != null) {
            mDSCallInfoList.add(DSCallInfo);
            if(mDSInit){
                dispatchStartupQueue();
            }
        } else {
            dispatchJavascriptCall(DSCallInfo);
        }
    }

    public <T> void evaluateScript(String method, Object[] args, final DSResultListener<T> DSResultListener) {
        DSCallInfo DSCallInfo = new DSCallInfo(method, mCallID.incrementAndGet(), args);
        if (DSResultListener != null) {
            mJsCallMap.put(DSCallInfo.callbackId, DSResultListener);
        }
        if (mDSInit) {
            dispatchJavascriptCall(DSCallInfo);
        } else {
            mDSCallInfoList.add(DSCallInfo);
        }
    }

    protected void dispatchJavascriptCall(DSCallInfo info) {
        String jsCall = String.format("window._handleMessageFromNative(%s)", info.toString());
        executeScript(jsCall);
    }

    protected void dispatchStartupQueue() {
        if (mDSCallInfoList != null && !mDSCallInfoList.isEmpty()) {
            List<DSCallInfo> copy = new ArrayList<>(mDSCallInfoList);
            mDSCallInfoList.clear();
            for (DSCallInfo info : copy) {
                dispatchJavascriptCall(info);
            }
        }
    }
}
